#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/sysfs.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include "spi_sh1106.h"

static struct sh1106_cdev* sh1106 = NULL;

static int sh1106_open(struct inode* inode, struct file* filp)
{
    struct sh1106_cdev* dev;
    dev = container_of(inode->i_cdev, struct sh1106_cdev, cdev);
	// 如果是非阻塞打开，尝试获取互斥体，获取失败直接返回，获取成功继续执行
	if (filp->f_flags & O_NONBLOCK) {
		// mutex_trylock和down_trylock的返回值意义相反
		if (!mutex_trylock(&dev->mutex))
			return -EBUSY;
	}
	else mutex_lock(&dev->mutex);

    // 复位oled
    gpio_set_value(dev->rst_pin, 0);
    // 延迟50毫秒
    mdelay(50);
    gpio_set_value(dev->rst_pin, 1);

    filp->private_data = dev;            
    return 0;
}
static ssize_t sh1106_write(struct file* filp, const char __user* buf, 
                            size_t size, loff_t* ppos)
{
    int ret = 0;
    struct spi_message m;
    struct spi_transfer t;
    char buffer[512] = {0};
    struct sh1106_cdev* dev = filp->private_data;
    if (size > sizeof(buffer)) return -EINVAL;

    ret = copy_from_user(buffer, buf, size);
    if (ret != 0) return -EFAULT;
#if 0
    ret = spi_write(dev->spi, buffer, size);
    if (ret != 0) return ret;
#else
    memset(&m, 0, sizeof(struct spi_message));
    memset(&t, 0, sizeof(struct spi_transfer));
    t.tx_buf = buffer;  // 发送缓冲区地址
    t.len = size;  // 发送数据的长度
    spi_message_init(&m);  // 初始化spi_message
    spi_message_add_tail(&t, &m);  // 将spi_transfer添加到spi_message
    ret = spi_sync(dev->spi, &m);  // 同步传输
    if (ret != 0) return -EFAULT;
#endif
    return size;
}
static int sh1106_release(struct inode* inode, struct file* filp)
{
    struct sh1106_cdev* dev;
    dev = container_of(inode->i_cdev, struct sh1106_cdev, cdev); 
    mutex_unlock(&dev->mutex);
    return 0;
}

// 控制spi传输的信息对oled来说是命令还是数据
static long sh1106_ioctl(struct file* filp, unsigned int cmd, unsigned long arg)
{
    struct sh1106_cdev* dev = filp->private_data;
    if (cmd == CMD)
        gpio_set_value(dev->dc_pin, 0); // spi传输的信息是命令
    else if(cmd == DATA)
        gpio_set_value(dev->dc_pin, 1); // spi传输的信息是数据
    else
        return -EINVAL;
    return 0;           
}

static const struct file_operations sh1106_ops = 
{
    .owner = THIS_MODULE,
    .open = sh1106_open,
    //.read = sh1106_read,
    .write = sh1106_write,
    .unlocked_ioctl = sh1106_ioctl,
    .release = sh1106_release
};
// 获取设备树中定义的复位、数据命令GPIO
static int get_gpio(struct sh1106_cdev* dev)
{
    int ret = 0;
    dev->rst_pin = of_get_named_gpio(dev->node, "rst-gpio", 0);
    if (dev->rst_pin < 0) {
        dev_err(&dev->spi->dev, "of_get_named_gpio rst-gpio failed, errno %d\n", 
                                dev->rst_pin);
        return dev->rst_pin;
    }
    ret = gpio_is_valid(dev->rst_pin);
    if (ret == 0) {
        dev_err(&dev->spi->dev, "gpio_is_valid rst-gpio failed, errno %d\n", ret);
        return -ENODEV;        
    }
    ret = gpio_request(dev->rst_pin, "rst-gpio");
    if (ret < 0) {
        dev_err(&dev->spi->dev, "gpio_request rst-gpio failed, errno %d\n", ret);
        return ret;         
    }
    // 设置rst引脚输出高点平
    gpio_direction_output(dev->rst_pin, 1);

    dev->dc_pin = of_get_named_gpio(dev->node, "dc-gpio", 0);
    if (dev->dc_pin < 0) {
        dev_err(&dev->spi->dev, "of_get_named_gpio dc-gpio failed, errno %d\n", ret);
        ret = dev->dc_pin;
        goto free_rst;
    }
    ret = gpio_is_valid(dev->dc_pin);
    if (ret == 0) {
        dev_err(&dev->spi->dev, "gpio_is_valid dc-gpio failed, errno %d\n", ret);
        ret = -ENODEV;
        goto free_rst;        
    }
    ret = gpio_request(dev->dc_pin, "dc-gpio");
    if (ret < 0) {
        dev_err(&dev->spi->dev, "gpio_request dc-gpio failed, errno %d\n", ret);
        goto free_rst;         
    }
    // 设置dc引脚输出高点平
    gpio_direction_output(dev->dc_pin, 1);
    return 0;
free_rst:
    gpio_free(dev->rst_pin);
    return ret;
}

static int sh1106_probe(struct spi_device* spi)
{
    int ret = 0;
    sh1106 = kzalloc(sizeof(struct sh1106_cdev), GFP_KERNEL);
    if (NULL == sh1106) {
        dev_err(&spi->dev, "kzalloc failed\n");
        return -ENOMEM;
    }
    sh1106->spi = spi;
    sh1106->node = spi->dev.of_node;

    ret = alloc_chrdev_region(&sh1106->devno, 0, DEV_NUM, NAME);
    if (ret != 0) {
        dev_err(&spi->dev, "alloc_chrdev_region error, errno %d\n", ret);
        goto free_sh1106;
    }
    dev_info(&spi->dev, "device major numbers %d, minor numbers %d\n",
                MAJOR(sh1106->devno), MINOR(sh1106->devno));    
    // 初始化字符设备和注册操作函数结构体
    cdev_init(&sh1106->cdev, &sh1106_ops);
    sh1106->cdev.owner = THIS_MODULE;
    ret = cdev_add(&sh1106->cdev, sh1106->devno, DEV_NUM);
    if (ret < 0) {
        dev_err(&spi->dev, "cdev_add error, errno %d\n", ret);
        goto unregister_devno;        
    }
    // 创建类和设备，便于自动生成设备节点
	sh1106->sh1106_class = class_create(THIS_MODULE, NAME);
	if (IS_ERR(sh1106->sh1106_class)) {
		ret = PTR_ERR(sh1106->sh1106_class);
        dev_err(&spi->dev, "class_create failed %d\n", ret);
		goto del_cdev;
	}
	sh1106->sh1106_device = device_create(sh1106->sh1106_class, 
                        NULL, sh1106->devno, NULL, NAME);
	if (IS_ERR(sh1106->sh1106_device)) {
		ret = PTR_ERR(sh1106->sh1106_device);
        dev_err(&spi->dev, "device_create failed %d\n", ret);
		goto clean_class;
	}
    ret = get_gpio(sh1106);
    if (ret != 0) goto clean_device;

    mutex_init(&sh1106->mutex);
    return 0;

clean_device:
	device_destroy(sh1106->sh1106_class, sh1106->devno);
clean_class: 
	class_destroy(sh1106->sh1106_class);    
del_cdev:
    cdev_del(&sh1106->cdev);
unregister_devno:
    unregister_chrdev_region(sh1106->devno, DEV_NUM);
free_sh1106:
    kfree(sh1106);
    sh1106 = NULL;
    return ret;
}

static int sh1106_remove(struct spi_device* spi)
{
    gpio_free(sh1106->dc_pin);
    gpio_free(sh1106->rst_pin);
	device_destroy(sh1106->sh1106_class, sh1106->devno);
	class_destroy(sh1106->sh1106_class);    
    cdev_del(&sh1106->cdev);
    unregister_chrdev_region(sh1106->devno, DEV_NUM);
    kfree(sh1106);
    sh1106 = NULL;
    return 0;
}

// 传统的设备匹配表
static const struct spi_device_id sh1106_id[] = {
    {"oled, ssh1106", 0},
    { }
};
// 设备树匹配表
static const struct of_device_id sh1106_of_match[] = {
    {.compatible = "oled, ssh1106"},
    { } // 最后一项必须为空
};

// 外设驱动结构体
static struct spi_driver sh1106_driver = {
    .id_table = sh1106_id,
    .probe = sh1106_probe,
    .remove = sh1106_remove,
    .driver = {
        .owner = THIS_MODULE,
        .name = NAME,
        .of_match_table = sh1106_of_match,
    }
};

#if 1
static __init int sh1106_init(void)
{
    int ret = 0;
    ret = spi_register_driver(&sh1106_driver);
    if (ret != 0) 
        pr_err("spi_sh1106 register failed %d\n", ret);
    return ret;
}
static __exit void sh1106_exit(void)
{
    spi_unregister_driver(&sh1106_driver);
}

module_init(sh1106_init);
module_exit(sh1106_exit);
#else
// 同时完成sh1106_init、sh1106_exit、module_init、module_exit的功能
module_spi_driver(sh1106_driver);
#endif
MODULE_LICENSE("GPL");
MODULE_AUTHOR("liyang.plus@foxmail.com");
